package io.github.annyylxyms.gyn.gateway.demo;

import cn.gyn.gynrpc.core.api.LoadBalancer;
import cn.gyn.gynrpc.core.api.RegistryCenter;
import cn.gyn.gynrpc.core.cluster.RoundRibbonLoadBalancer;
import cn.gyn.gynrpc.core.meta.InstanceMeta;
import cn.gyn.gynrpc.core.meta.ServiceMeta;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * hello handler
 * 分离逻辑和聚合点
 * @author annyylxyms
 * @create 2024.12.8
 */
@Component
public class GatewayHandler {
    @Autowired
    RegistryCenter rc;
    LoadBalancer<InstanceMeta> loadBalancer = new RoundRibbonLoadBalancer<>();

    /**
     * GatewayRouter返回的路由函数，调用handle方法
     * @param request
     * @return
     */
    Mono<ServerResponse> handle(ServerRequest request) {
        //1、通过请求拿到路径里service的全限定名,substring从第四个字符取到结束"/gw/**"
        String service = request.path().substring(4);
        //从注册中心拿到服务实例的列表：两种办法：1、可以照搬注册中心代码过来，2、也可以打包注册中心的jar包引入进来
        //2、通过rc拿到所有活着的服务实例（这里可以优化加fetchAll缓存、加动态订阅、增量订阅）
        ServiceMeta serviceMeta = ServiceMeta.builder().name(service)
                .app("app1").env("dev").namespace("public").build();
        List<InstanceMeta> instanceMetas = rc.fetchAll(serviceMeta);
        //3、先简化处理，获取第一个实例url
        //String url = instanceMetas.get(0).toUrl();
        //换成LB看看网关是不是确实取到了多个
        InstanceMeta instanceMeta = loadBalancer.choose(instanceMetas);
        System.out.println("instanceMetas size = " + instanceMetas.size() + " ,loadBalancer choose:" + instanceMeta);
        String url = instanceMeta.toUrl();

        //4、拿到请求的报文（POST请求里的body请求报文）
        Mono<String> requestMono = request.bodyToMono(String.class);

        //异步，防止requestMono为空调用后续的逻辑，分两次回调
        return requestMono.flatMap(x -> invokeFromRegistry(x, url));

        //return ServerResponse.ok().body(Mono.just("hello,gateway."), String.class);
        //上行另一种写法：bodyValue方法，直接返回响应体，不用再封装
        //return ServerResponse.ok().bodyValue("hello,gyn gateway.");
        //mono继承了core publisher，所以可以用mono的subscribe方法来订阅，但是一般不用，因为是异步的
        //mono 和 flux 都是publisher，都是数据提供者，可以订阅，但是mono只能订阅一次，而flux可以订阅多次
    }

    private static Mono<ServerResponse> invokeFromRegistry(String x, String url) {
        //5、通过webclient发送POST请求
        WebClient client = WebClient.create(url);
        Mono<ResponseEntity<String>> entity = client.post().header("Content-Type", "application/json")
                .bodyValue(x).retrieve().toEntity(String.class);

        //6、通过entity获取响应报文
        Mono<String> body = entity.map(ResponseEntity::getBody);
        body.subscribe(source -> System.out.println("response:" + source));
        //7、组装响应报文
        return ServerResponse.ok()
                .header("Content-Type", "application/json")
                .header("gyn.gw.version", "v1.0.0")
                .body(body, String.class);
    }
}
